﻿/*
 *  Author: Louis A. Leskoviansky
 *  Company: Artemis Solutions Group (http://www.artemis-solutions.com)
 */


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;

namespace AjaxControlFramework
{
    public static class AjaxControlUtils
    {
        //------// Properties \\--------------------------------------------\\
        public delegate void CreateChildControlsMethod(Control control);

        private static object CreateChildControlsLock = new object();
        private static CreateChildControlsMethod _createChildControls = null;
        public static CreateChildControlsMethod CreateChildControls
        {
            get
            {
                if (_createChildControls == null)
                {
                    lock (CreateChildControlsLock)
                    {
                        if (_createChildControls == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "SaveViewStateRecursive".
                            MethodInfo createChildControlsMethodInfo = typeof(Control).GetMethod("CreateChildControls", BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("CreateChildControls", typeof(void), new Type[] { typeof(Control) }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Callvirt, createChildControlsMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _createChildControls = (CreateChildControlsMethod)dynamicMethod.CreateDelegate(typeof(CreateChildControlsMethod));
                        }
                    }
                }

                return _createChildControls;
            }
        }
        //------\\ Properties //--------------------------------------------//



        //------// Methods \\-----------------------------------------------\\
        public static List<HtmlControl> FindChildControlsByTagName(Control parent, string tagName)
        {
            List<HtmlControl> controls = new List<HtmlControl>();
            FindChildControlsByTagName(parent, tagName.ToLower(), ref controls);

            return controls;
        }


        private static void FindChildControlsByTagName(Control parent, string tagName, ref List<HtmlControl> controls)
        {
            if (parent.Controls.Count == 0) { return; }

            foreach (HtmlControl childControl in parent.Controls.OfType<HtmlControl>())
            {
                if (childControl.TagName.ToLower() == tagName)
                {
                    controls.Add(childControl);
                }

                FindChildControlsByTagName(childControl, tagName, ref controls);
            }
        }


        public static Control FindTopMostParent(Control child)
        {
            if (child == null) { return null; }

            Control topMostControl = child;
            FindTopMostParent(child, ref topMostControl);

            return topMostControl;
        }


        private static Control FindTopMostParent(Control child, ref Control topMostParent)
        {
            Control parent = child.Parent;

            if (parent == null)
            {
                return child;
            }
            else
            {
                topMostParent = parent;
                return FindTopMostParent(parent, ref topMostParent);
            }
        }


        public static Control FindTopMostParent(Page child)
        {
            if (child == null) { return null; }

            Control topMostControl = child;
            FindTopMostParent(child, ref topMostControl);

            return topMostControl;
        }


        private static Control FindTopMostParent(Page child, ref Control topMostParent)
        {
            MasterPage parent = child.Master;

            if (parent == null)
            {
                return child;
            }
            else
            {
                topMostParent = parent;
                return FindTopMostParent(parent, ref topMostParent);
            }
        }


        private static Control FindTopMostParent(MasterPage child, ref Control topMostParent)
        {
            MasterPage parent = child.Master;

            if (parent == null)
            {
                return child;
            }
            else
            {
                topMostParent = parent;
                return FindTopMostParent(parent, ref topMostParent);
            }
        }


        public static T FindParentControlByType<T>(Control child) where T : class
        {
            T parentControl = default(T);
            FindParentControlByType<T>(child, ref parentControl);

            return parentControl;
        }


        private static void FindParentControlByType<T>(Control child, ref T parentControl) where T : class
        {
            Control parent = child.Parent;

            if (parent == null)
            {
                parentControl = default(T);
            }
            else
            {
                if (parent is T)
                {
                    parentControl = parent as T;
                }
                else
                {
                    FindParentControlByType<T>(parent, ref parentControl);
                }
            }
        }


        public static List<T> FindChildControlsByType<T>(Control parent) where T : class
        {
            List<T> controls = new List<T>();
            FindChildControlsByType<T>(parent, ref controls);

            return controls;
        }


        private static void FindChildControlsByType<T>(Control parent, ref List<T> controls) where T : class
        {
            controls.AddRange(parent.Controls.OfType<T>());

            foreach (Control childControl in parent.Controls)
            {
                FindChildControlsByType<T>(childControl, ref controls);
            }
        }


        public static List<Control> FindControlsThatRequireControlState(Page parentPage, Control parentControl)
        {
            List<Control> controls = new List<Control>();
            FindControlsThatRequireControlState(parentPage, parentControl, ref controls);

            return controls;
        }


        private static void FindControlsThatRequireControlState(Page parentPage, Control parentControl, ref List<Control> controls)
        {
            if (parentPage.RequiresControlState(parentControl))
            {
                controls.Add(parentControl);
            }

            foreach (Control childControl in parentControl.Controls)
            {
                FindControlsThatRequireControlState(parentPage, childControl, ref controls);
            }
        }


        public static Type ResolveControlType(string fullyQualifiedTypeName)
        {
            Type controlType = Type.GetType(fullyQualifiedTypeName, false, true);

            if (controlType == null)
            {
                string unqualifiedTypeName = DetermineUnqualifiedTypeName(fullyQualifiedTypeName);

                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

                foreach (Assembly assembly in assemblies)
                {
                    controlType = assembly.GetType(unqualifiedTypeName, false, true);

                    if (controlType != null) { break; }
                }
            }

            return controlType;
        }


        public static string DetermineUnqualifiedTypeName(string fullyQualifiedTypeName)
        {
            int indexOfFirstComma = fullyQualifiedTypeName.IndexOf(',');

            if (indexOfFirstComma > -1)
            {
                return fullyQualifiedTypeName.Substring(0, indexOfFirstComma).Trim();
            }

            return fullyQualifiedTypeName.Trim();
        }
        //------\\ Methods //-----------------------------------------------//
    }
}
